package table_Mixage;


/**
 *  Piste 
 *  Description: Classe principale du modele,
 *	
 *	Contenu:
 *		- charge les fichiers
 *		- crée les canaux
 *		- sauvegarde 
 *
 *
 * @author  François Miller / Peloux Arnaud</a>
 * @version 1.0
 */

import java.io.*;
import java.util.*;

public class Piste {

    private ArrayList<Canal> lesCanaux;
    private  int frequence;
    private  int resolution;	// nombre d'octet pour un echantillon
    private boolean signe ; // les échantillons sont signés(true) ou non signés(false)

//----------------------------------------Guetteurs/Setteurs----------------------------------------
   
    public int getFrequence(){
    	return frequence;
    }   
    public void setFrequence(int frq){
    	this.frequence = frq;
    	for (Canal c : lesCanaux)
	    	c.setFrequence(frq);
    	
    }
    public int getResolution(){
    	return resolution;
    }
    public int getNbCanaux(){
    	return lesCanaux.size();
    }
    public boolean getSigne(){
    	return signe;
    }
    public int getNbEch(){
    	int size=0;
    	for (int i=0; i< lesCanaux.size();i++)
    		size=size+lesCanaux.get(i).getNbech();
    		
    	return size;
    }
    public Canal getCanal(int n){
    	return lesCanaux.get(n);
    }
    public float getDuree(int nbCanal){
    	return((float)getCanal(nbCanal).getNbech()/(float)frequence);
    	
    }
    

  //----------------------------------------Constructeurs----------------------------------------    
    public Piste(){
		lesCanaux = new ArrayList<Canal>();
		lesCanaux.add(new Canal());
		this.frequence = 16000;
		resolution = 2;
		signe = true;
	
    } 
    public Piste(int nbcanaux, int frequence, int resolution,  long size){
		lesCanaux = new ArrayList<Canal>(nbcanaux);
		for (int i=0 ; i < nbcanaux ; i++)
		    lesCanaux.add(new Canal(size/nbcanaux,frequence));	// Création des canaux	
	
		this.resolution =resolution ;
		this.frequence = frequence;
		signe = true;
	
    }

  //----------------------------------------Ajout échantillon----------------------------------------   

   
    protected byte[] echantillonToByte(int echcour, int resolution){
    	byte[] buf = new byte[resolution];
	    	for(int i=0; i< resolution; i++){
	    		buf[i] = (byte) (echcour );
	    		echcour = echcour >>8 ;
	    	} 	
    	return buf ;
    }
    protected int creerEchantillon(byte[] buffer, int resolution){
        int i = resolution-1;
        int res = buffer[i]; // signé
        
        
        while (i > 0)
        {
            i--;
            res <<= 8;
            if ( buffer[i] < 0) // non signé car il y a que le bit de poids fort est signé 
                res += 256; 
            res += buffer[i];    
        }
        return res;
        }
    
    //----------------------------------------Chargement depuis Fichiers----------------------------------------     
   
     public  Piste loadFromRawFile(String filename,int nbcanaux, int frequence, int resolution){
    	 Piste res = null;
		InputStream fis;
		int echantillonCour;
		int numCanalCour = 0;
		byte[] buffer;
		int n;
		try{
		    File finput = new File(filename);
		    fis = new FileInputStream(finput);
		    res = new Piste(nbcanaux, frequence, resolution, finput.length()/resolution);
		    buffer = new byte[resolution];
		    n = fis.read(buffer,0,resolution);
		    while (n>0){
		    	
		    	echantillonCour = creerEchantillon(buffer, resolution);
		    	res.lesCanaux.get(numCanalCour).addEchantillon(echantillonCour);
		    	numCanalCour = (numCanalCour + 1) % nbcanaux;
		    	n = fis.read(buffer,0,resolution);
		    }
		    fis.close();
		}catch(IOException e){}
			return res;
    }
     public  Piste loadFromWavFile(String filename){
    	 Piste res = null;
    	InputStream fis;
    	int echantillonCour,frequenceCour,nbCanauxCour,resolutionCour;
    	int numCanalCour = 0;
    	byte[] buffer;
    	byte[] entete = new byte[44];
    	int n;
    	try{
    		File finput = new File(filename);
    	    fis = new FileInputStream(finput);
    	    fis.read(entete,0,44);
    	    if (!testEntete(entete)) {fis.close();return(null);}	
    	    
    	    /** récupération des valeurs dans l'entete*/
    	    
			frequenceCour 	= 		(0xFF & entete[27]) << 24 | (0xFF & entete[26]) << 16 | (0xFF & entete[25]) << 8 | (0xFF & entete[24]) ;
			nbCanauxCour 	=		(0xFF & entete[23]) << 8 | (0xFF & entete[22]) ;
			resolutionCour 	=		((0xFF & entete[35]) << 8 | (0xFF & entete[34]))/8;

			
    	    res = new Piste(nbCanauxCour, frequenceCour, resolutionCour, (finput.length()-44)/resolutionCour);
    	    buffer = new byte[resolutionCour];
    	    n = fis.read(buffer,0,resolutionCour); 
    	    while (n>0){
    			echantillonCour = creerEchantillon(buffer, resolutionCour);
    			res.lesCanaux.get(numCanalCour).addEchantillon(echantillonCour);
    			numCanalCour = (numCanalCour + 1) % nbCanauxCour;
    			n = fis.read(buffer,0,resolutionCour);
    	    }
    	    fis.close();
    	}catch(IOException e){}
    		return res;
            	
    	
    }
    
    
    //----------------------------------------Sauvegarde dans fichier----------------------------------------
    
     public void saveToRawFile(String filename,int debut, int fin){
		OutputStream fos;
		int echantillonCour;
		int numCanalCour = 0;
		byte[] buffer;
		int n=0;
		refresh();
    	try{
		    File fOutput = new File(filename);
		    fos = new FileOutputStream(fOutput);
		    buffer = new byte[resolution];
		    initLecture();
		    
		    while (n < getNbEch()& n<fin){
	    		numCanalCour = 0;
			    while (numCanalCour < getNbCanaux()& n<fin){
			    	echantillonCour =lesCanaux.get(numCanalCour).getEchantillon();
			    	if (n>=debut){
				    	buffer=echantillonToByte(echantillonCour, resolution);
				    	fos.write(buffer);
				    	}
			    	numCanalCour = (numCanalCour + 1) % getNbCanaux();
			    	n++;}
		    }
		    fos.close();
		}catch(IOException e){}
    }
     public void saveToWavFile(String filename,int debut , int fin){
		OutputStream fos;
		int echantillonCour;
		int numCanalCour = 0;
		byte[] entete = new byte[44], buffer;
		int n=0;
		entete=createEntete(debut,fin);
		refresh();
    	try{
		    File fOutput = new File(filename);
		    fos = new FileOutputStream(fOutput);
		    initLecture();
		    fos.write(entete);
		   
		    while (n < getNbEch()& n<fin){
	    		numCanalCour = 0;
	    		while (numCanalCour < getNbCanaux()& n<fin){	
	    		    echantillonCour = getCanal(numCanalCour).getEchantillon();
	    		    if (n>= debut){
		    		    buffer = echantillonToByte(echantillonCour,resolution );
		    		    fos.write(buffer);}
	    		    numCanalCour = (numCanalCour + 1 ) % getNbCanaux();
	    		    n++;}
		    }
		    fos.close();
		}catch(IOException e){}
    } 
 
 

  //----------------------------------------Gestion des Entêtes---------------------------------------- 
     private  boolean testEntete(byte[] entete){

		String 	fileTypeBlocID,fileFormatID, formatBlocID, dataBlocID;
		 
		fileTypeBlocID=	String.valueOf((char)entete[0])+String.valueOf((char)entete[1])+
							String.valueOf((char)entete[2])+String.valueOf((char)entete[3]);
	
		fileFormatID= 	String.valueOf((char)entete[8])+String.valueOf((char)entete[9])+
							String.valueOf((char)entete[10])+String.valueOf((char)entete[11]);

		formatBlocID= 	String.valueOf((char)entete[12])+String.valueOf((char)entete[13])+String.valueOf((char)entete[14])+String.valueOf((char)entete[15]);
			
		dataBlocID = 	String.valueOf((char)entete[36])+String.valueOf((char)entete[37])+
							String.valueOf((char)entete[38])+String.valueOf((char)entete[39]);

	   
		return (fileTypeBlocID.equals("RIFF") & fileFormatID.equals("WAVE")
				& formatBlocID.equals("fmt ") & dataBlocID.equals("data"));
    }
   
     private  byte[] createEntete(int debut,int fin){
    	byte entete[]= new byte[44];
    	byte temp4[]= new byte[4];
    	byte temp2[]= new byte[2];
    	int j=0;
    	entete [0] = 'R';
    	entete [1] = 'I';
    	entete [2] = 'F';
    	entete [3] = 'F';
    	
    	/**
    	 * FileSize sur 4 octets :
    	 */
 
    	temp4=echantillonToByte((((fin-debut)*getNbCanaux())+36),4);
    	j=0;
    	for(int i =4;i<7;i++){entete[i]=temp4[j];j++;}
    	
    	
    	entete [8] = 'W';
    	entete [9] = 'A';
    	entete [10]= 'V';
    	entete [11]= 'E';	
    	
    	entete [12] = 'f';
    	entete [13] = 'm';
    	entete [14] = 't';
    	entete [15]	= ' ';
    	
    	/**
    	 * BlocSize sur 4 octets :
    	 */
    	temp4=echantillonToByte(resolution*8,4);
    	j=0;
    	for(int i =16;i<19;i++){entete[i]=temp4[j];j++;}
    	
    	/**
    	 * Audio format sur 2 octets :
    	 */
    	temp2=echantillonToByte(1,2);
    	j=0;
    	for(int i =20;i<21;i++){entete[i]=temp2[j];j++;}
    	
    	
    	/**
    	 * Nombre Canaux sur 2 octets :
    	 */
    	temp2=echantillonToByte(lesCanaux.size(),2);
    	
    	j=0;
    	for(int i =22;i<23;i++){entete[i]=temp2[j];j++;}
    	
    	/**
    	 * Frequence sur 4 octets :
    	 */
    	
    	temp4=echantillonToByte(getFrequence(),4);
    	j=0;
    	for(int i =24;i<27;i++){entete[i]=temp4[j];j++;}
    	
    	/**
    	 * Nombre d'octets à lire par seconde sur 4 octets :
    	 */
    	
    	temp4=echantillonToByte(getFrequence()*resolution*lesCanaux.size(),4);
    	j=0;
    	for(int i =28;i<31;i++){entete[i]=temp4[j];j++;}
    	
    	/**
    	 * Nombre d'octets par bloc d'échantillonnage sur 2 octets :
    	 */
    	
    	temp2=echantillonToByte(lesCanaux.size() * resolution,2);
    	j=0;
    	for(int i =32;i<33;i++){entete[i]=temp2[j];j++;}
    	
    	
    	/**
    	 * Nombre de bits utilisés pour le codage des echantillons sur 2 octets :
    	 */

    	temp2=echantillonToByte(resolution*8,2);
    	j=0;
    	for(int i =34;i<35;i++){entete[i]=temp2[j];j++;}
    	
    	
    	entete [36] = 'd';
    	entete [37] = 'a';
    	entete [38] = 't';
    	entete [39] = 'a';	
    	
    	/**
    	 * Taille des Données sur 4 octets :
    	 */

    	temp4=echantillonToByte((fin-debut)*resolution,4);
    	j=0;
    	for(int i =40;i<44;i++){entete[i]=temp4[j];j++;}
    	
    	return entete;
    }

   //----------------------------------------Opérations générales sur les canaux----------------------------------------
     void refresh(){
    	/*
    	 * Vérification du nombre de canaux, bourrage si un canal et plus grand qu'un autre
    	 * a étoffer au besoin	supprimer le canal ( mettre dans un temp et le recréer)
    	 */
    	
    	int canalPlusGrand =0, delta;


    	for(int i=0; i< getNbCanaux()-1; i++){
    	 if (lesCanaux.get(i).getNbech()<lesCanaux.get(i+1).getNbech())
    				canalPlusGrand =i+1;	
    		}
 
    	for(int i=0; i< getNbCanaux()-1; i++){
    		
    			delta =lesCanaux.get(canalPlusGrand).getNbech()-lesCanaux.get(i).getNbech();
    			if (delta!=0)
    				lesCanaux.get(i).bourrage (delta);	}
    }
    	
     public void addCanal (Canal c){
	   lesCanaux.add(c);
	   refresh();
   } 	
     public void removeCanal (int nbCanal){
  	   lesCanaux.remove(nbCanal);
     } 
    
     public Piste synchronizeFrequence(Piste p){
    	int i=0;
    	 for (@SuppressWarnings("unused") Canal c : p.lesCanaux){
		    p.lesCanaux.get(i).synchronizeFrequence(this.frequence);
		    i++;
    	 }
    	 refresh();
    	 return p;
     }
     
     protected void initLecture(){
		for (Canal c : lesCanaux)
		    	c.initLecture();
			refresh();
   }
   
     protected void initLecture(int deb){
		for (Canal c : lesCanaux)
		    	c.initLecture(deb);
		refresh();
    }
   
     public void controlVolume(double val){
    	 for (Canal c : lesCanaux)
		    	c.controlVolume(val);
			refresh();
     }
    
     public String toString(){
    	 refresh();
    	 return (" Nombre d'échantillons: "+getNbEch()
    			 +" Nombre de Canaux: "+getNbCanaux()+" Signe: "+getSigne()+" Duree: "+getDuree(0)+" Fréquence :" +frequence);
     }
     public boolean Equals(Piste p){ 
    	 
    	 for (int n=0; n<lesCanaux.size();n++ ){ 
 		   if (lesCanaux.get(n).equals(p.getCanal(n)) != true)
 				   return false;
     	 }
    	 return (p.resolution == resolution & p.frequence == frequence &
    			 p.signe == signe & p.getNbEch()== getNbEch());
     }
}